# 插件 API

上一节我们介绍了插件的基本结构，本节我们来介绍插件的 API，帮助你更细致地了解插件功能。

### globalStyles

- **类型**：`string`

用于添加全局样式，传入一个样式文件的绝对路径，使用方式如下：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';
import path from 'path';

export function pluginForDoc(): RspressPlugin {
  // 样式路径
  const stylePath = path.join(__dirname, 'some-style.css');
  return {
    // 插件名称
    name: 'plugin-name',
    // 全局样式的路径
    globalStyles: path.join(__dirname, 'global.css'),
  };
}
```

比如你想修改主题色，可以通过添加全局样式来实现：

```css title="global.css"
:root {
  --rp-c-brand: #ffa500;
  --rp-c-brand-dark: #ffa500;
  --rp-c-brand-darker: #c26c1d;
  --rp-c-brand-light: #f2a65a;
  --rp-c-brand-lighter: #f2a65a;
}
```

### globalUIComponents

- **类型**：`(string | [string, object])[]`

用于添加全局组件，传入一个数组，数组中的每一项都是一个组件的绝对路径，使用方式如下：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  // 组件路径
  const componentPath = path.join(__dirname, 'foo.tsx');
  return {
    // 插件名称
    name: 'plugin-comp',
    // 全局组件的路径
    globalUIComponents: [componentPath],
  };
}
```

`globalUIComponents` 的每一项可以是一个字符串，代表组件的文件路径；也可以是一个数组，第一项为组件的文件路径，第二项为组件的 props 对象，比如：

```ts title="rspress.config.ts"
import { defineConfig } from '@rspress/core';
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  // component path
  const componentPath = path.join(__dirname, 'foo.tsx');
  return {
    // plugin name
    name: 'plugin-comp',
    globalUIComponents: [
      [
        path.join(__dirname, 'components', 'MyComponent.tsx'),
        {
          foo: 'bar',
        },
      ],
    ],
  };
}
```

import GlobalUIComponents from '../../fragments/global-ui-components.mdx';

<GlobalUIComponents />

### builderConfig

- **类型**：`RsbuildConfig`

Rspress 使用 [Rsbuild](https://github.com/web-infra-dev/rsbuild) 作为构建工具。通过 `builderConfig` 可以对 Rsbuild 进行配置，具体的配置项可以参考 [Rsbuild - 配置](https://rsbuild.rs/zh/config/)。

> 当然，如果你想要直接配置 Rspack，也可以通过 `builderConfig.tools.rspack` 进行配置。

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(slug: string): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 构建阶段的全局变量定义
    builderConfig: {
      source: {
        define: {
          SLUG: JSON.stringify(slug),
        },
      },
      tools: {
        rspack(options) {
          // 修改 rspack 的配置
        },
      },
    },
  };
}
```

> 查看 [构建配置](/api/config/config-build) 了解更多。

### config

- **类型**：`(config: DocConfig, utils: ConfigUtils, isProd: boolean) => DocConfig | Promise<DocConfig>`

其中，`ConfigUtils` 的类型定义如下：

```ts
interface ConfigUtils {
  addPlugin: (plugin: RspressPlugin) => void;
  removePlugin: (pluginName: string) => void;
}
```

用于修改 Rspress 本身的配置，比如你想要修改文档的标题，可以通过 `config` 来实现：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 扩展 Rspress 本身的配置
    config(config) {
      return {
        ...config,
        title: '新的文档标题',
      };
    },
  };
}
```

如果涉及到添加和删除插件，需要通过 `addPlugin` 和 `removePlugin` 来实现：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名字
    name: 'plugin-name',
    // 扩展 Rspress 本身的配置
    config(config, utils) {
      // 添加插件
      utils.addPlugin({
        name: 'plugin-name',
        // ...插件的其他配置
      });
      // 通过插件名称来删除插件
      utils.removePlugin('plugin-name');
      return config;
    },
  };
}
```

### beforeBuild/afterBuild

- **类型**：`(config: DocConfig, isProd: boolean) => void | Promise<void>`

用于在文档构建之前/之后执行一些操作，第一个参数是文档的配置，第二个参数是当前是否是生产环境。使用方式如下：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 在构建之前执行的钩子
    async beforeBuild(config, isProd) {
      // 这里可以执行一些操作
    },
    // 在构建之后执行的钩子
    async afterBuild(config, isProd) {
      // 这里可以执行一些操作
    },
  };
}
```

:::tip 提醒
在 `beforeBuild` 钩子执行时，已经经过了所有插件的 `config` 插件处理，因此 config 参数已经代表了最终的文档配置。
:::

### markdown

- **类型**：`{ remarkPlugins?: Plugin[]; rehypePlugins?: Plugin[]; globalComponents?: string[] }`

用于扩展 Markdown/MDX 的编译能力，如果你想要添加自定义的 remark/rehype 插件以及 MDX 里的全局组件，可以通过 `markdown` 配置来实现：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 扩展 Markdown/MDX 编译能力
    markdown: {
      // 启用 JS 版本的编译器以支持外部插件
      mdxRs: false,
      remarkPlugins: [
        // 添加自定义的 remark 插件
      ],
      rehypePlugins: [
        // 添加自定义的 rehype 插件
      ],
      globalComponents: [
        // 为 MDX 注册全局组件
      ],
    },
  };
}
```

### extendPageData

- **类型**：`(pageData: PageData) => void | Promise<void>`

用于扩展页面数据，比如你想要在页面数据中添加一些自定义的属性，可以通过 `extendPageData` 来实现：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 扩展页面数据
    extendPageData(pageData, isProd) {
      // 你可以往 pageData 对象上添加或者修改属性
      pageData.a = 1;
    },
  };
}
```

在扩展页面数据之后，你可以在主题中通过 `usePageData` 这个 hook 来访问页面数据。

```tsx
import { usePageData } from '@rspress/core/runtime';

export function MyComponent() {
  const { page } = usePageData();
  // page.a === 1
  return <div>{page.a}</div>;
}
```

### addPages

- **类型**：`(config: UserConfig) => AdditionalPage[] | Promise<AdditionalPage[]>`

其中，`config` 为 `rspress.config.ts` 配置文件中导出的 `doc` 属性值，`AdditionalPage` 的类型定义如下：

```tsx
interface AdditionalPage {
  routePath: string;
  filepath?: string;
  content?: string;
}
```

主要用来添加额外的页面，你可以在 `addPages` 函数中返回一个数组，数组中的每一项都是一个页面的配置，你可以通过 `routePath` 来指定页面的路由，通过 `filepath` 或者 `content` 来指定页面的内容。比如：

```tsx
import path from 'path';
import type { RspressPlugin } from '@rspress/core';

export function docPluginDemo(): RspressPlugin {
  return {
    name: 'add-pages',
    addPages(config, isProd) {
      return [
        //  支持真实文件的绝对路径(filepath)，这样会读取磁盘中的 md(x) 内容
        {
          routePath: '/filepath-route',
          filepath: path.join(__dirname, 'blog', 'index.md'),
        },
        //  支持通过 content 参数直接传入 md(x) 内容
        {
          routePath: '/content-route',
          content: '# Demo2',
        },
      ];
    },
  };
}
```

`addPages` 接受两个参数，`config` 为当前文档站的配置，`isProd` 表示是否为生产环境。

### routeGenerated

- **类型**：`(routeMeta: RouteMeta[]) => void | Promise<void>`

这这个钩子中，你可以拿到所有的路由信息，每一项路由信息的结构如下:

```ts
export interface RouteMeta {
  // 路由
  routePath: string;
  // 文件绝对路径
  absolutePath: string;
  // 页面名称，作为打包产物文件名的一部分
  pageName: string;
  // 语言
  lang: string;
}
```

例子:

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-routes',
    // 在构建之后执行的钩子
    async routeGenerated(routes, isProd) {
      // 这里可以拿到 routes 数组，执行一些操作
    },
  };
}
```

### addRuntimeModules

- **类型**：`(config: UserConfig, isProd: boolean) => Record<string, string> | Promise<Record<string, string>>;`

用于添加额外的运行时模块，比如你想要在文档中使用到某些编译时的信息，可以通过 `addRuntimeModules` 来实现：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 添加额外的运行时模块
    async addRuntimeModules(config, isProd) {
      const fetchSomeData = async () => {
        // 模拟异步请求
        return { a: 1 };
      };
      const data = await fetchSomeData();
      return {
        'virtual-foo': `export default ${JSON.stringify(data)}`,
      };
    },
  };
}
```

这样你就可以在运行时组件中使用 `virtual-foo` 模块了：

```jsx
import myData from 'virtual-foo';

export function MyComponent() {
  return <div>{myData.a}</div>;
}
```

:::tip 提醒

该 hook 在 `routeGenerated` 之后执行。

:::

### i18nSource

- **类型**：`(source: Record<string, Record<string, string>>) => Record<string, Record<string, string>> | Promise<Record<string, Record<string, string>>>`

用于添加或修改国际化文案数据。你可以通过这个 hook 来扩展或修改主题的国际化文案。

参数 `source` 是一个对象，其结构为：

```ts
{
  [textKey: string]: {
    [locale: string]: string;
  }
}
```

其中第一层的 `textKey` 是文案的键名，第二层的 `locale` 是语言代码（如 `zh`、`en`），值是对应语言的翻译文本。

使用示例：

```tsx title="plugin.ts"
import type { RspressPlugin } from '@rspress/core';

export function pluginForDoc(): RspressPlugin {
  return {
    // 插件名称
    name: 'plugin-name',
    // 添加或修改国际化文案
    i18nSource(source) {
      // 添加新的文案
      return {
        ...source,
        customKey: {
          zh: '自定义文案',
          en: 'Custom Text',
        },
        anotherKey: {
          zh: '另一个文案',
          en: 'Another Text',
        },
      };
    },
  };
}
```

如果你的插件同时提供了运行时组件，你可以通过 `useI18n` hook 来使用这些文案：

```tsx
import { useI18n } from '@rspress/core/runtime';

export function MyComponent() {
  const t = useI18n();
  return <div>{t('customKey')}</div>;
}
```
